#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <errno.h>
#include <error.h>
#include "Debug.h"
#include "xmlsock.h"
#include "utlist.h"
#include "loadflags.h"
#include "semproc_lock.h"

#define MAX_XML_COUNT          5
#define MAX_FILEPATHNAME_LEN   128
#define MAX_PROCESS_COUNT      8
#define MAX_NAME_LEN           24
#define SHMEM_KEY              4098709
#define SHMEM_DEBUG_PRINT      0

typedef struct st_NodeName
{
    char szName[MAX_NAME_LEN];
    int  Id;
    struct st_NodeName *next;
}NodeName;

typedef struct st_shmInfo
{
    int  pid[MAX_PROCESS_COUNT];
    char procname[MAX_PROCESS_COUNT][MAX_NAME_LEN];
    char xmlname[MAX_XML_COUNT][MAX_NAME_LEN];
    int loadflags[MAX_XML_COUNT][MAX_PROCESS_COUNT];
}ShmInfo;

NodeName    *g_monitorXmlHead = NULL;


int namelist_uniadd(NodeName **phead, const char *xmlname, int nId);
int namelist_find(NodeName *phead, const char *xmlname, NodeName **out);
int namelist_add_p(NodeName **phead, const char *xmlname, int nId);
int namelist_del(NodeName **phead, const char *xmlname);
int namelist_count(NodeName *phead);

char *filenamestrippath(const char *xmlname);
int getProcessName(char * szName, int nMaxLen);
int isProcRunning(int pid);


// shmem
//int shmem_lock();
//int shmem_unlock();

void *shmem_getmem();
int shmem_printmem();

int shmem_getxmlIndex(const char *xmlname);
int shmem_addxmlname(const char *xmlname);


int shmem_getprocIndex();
int shmem_uniaddname(void *paddr, const int nMaxNameCount, const char *addname, int *paddrId, int id);
int shmem_addprocname(const char *addname, int pid);

int shmem_bzero();
int shmem_clearxmlload(const char *xmlfilepathname);
int shmem_setxmlload(const char *xmlfilepathname);
int shmem_getxmlload(const char *xmlfilepathname);



// id start from 0
int xmlname2Id(const char *name);
int procname2Id(const char *name);


char *filenamestrippath(const char *xmlname)
{
    if(NULL == xmlname || strlen(xmlname) <= 0)
    {
        return NULL;
    }
    // remove path : /usr/bin/
    char *pBar = strrchr(xmlname, '/');
    if(NULL == pBar)
    {
        Safe_Printf("error name=%s", xmlname);
        return NULL;
    }
    return pBar + 1;
}


int namelist_uniadd(NodeName **phead, const char *xmlname, int nId)
{
    if(NULL == phead || NULL == xmlname || strlen(xmlname) <= 0)
    {
        Safe_Printf("error phead.ptr = %p,%p", phead, xmlname);
        return -1;
    }
    NodeName *out = NULL;
    int ret = namelist_find(*phead, xmlname, &out);
    if(ret >= 0)
    {
        Safe_Printf("xml exist.%s", xmlname);
        return 0;
    }
    NodeName *add = (NodeName *)malloc(sizeof(NodeName));
    snprintf(add->szName, sizearray(add->szName), "%s", xmlname);
    add->Id = nId;
    LL_APPEND(*phead, add);
    return 0;
}

int namecmp(NodeName *a, NodeName *b) 
{
    if(NULL == a || NULL == b)
        return 0;
    return strcmp(a->szName,b->szName);
}

int namelist_find(NodeName *phead, const char *xmlname, NodeName **out)
{
    if(NULL == phead || NULL == out)
    {
        Safe_Printf("error phead.ptr = %p,%p", phead, out);
        return -1;
    }
    *out = NULL;
    NodeName elt;
    snprintf(elt.szName, MAX_NAME_LEN, "%s", xmlname);
    LL_SEARCH(phead, *out, &elt, namecmp);
    if(NULL == *out)
    {
        Safe_Printf("xml not exist.%s", xmlname);
        return -1;
    }
    return 0;
}

int namelist_del(NodeName **phead, const char *xmlname)
{
    if(NULL == phead || NULL == *phead || NULL == xmlname || strlen(xmlname) <= 0)
    {
        return -1;
    }
    NodeName *pout = NULL;
    int ret = namelist_find(*phead, xmlname, &pout);
    if(ret < 0 || NULL == pout)
    {
        Safe_Printf("error not find %s", xmlname);
        return -1;
    }
    LL_DELETE(*phead, pout);
    return 0;
}

int namelist_print(NodeName *phead)
{
    NodeName *elt = NULL, *tmp = NULL;
    Safe_Printf("print list.............");
    DL_FOREACH_SAFE(phead, elt, tmp)
    {
        if(NULL == elt)
        {
            continue ;
        }
        Safe_Printf("--  %s,%d", elt->szName, elt->Id);
    }
    return 0;
}

int namelist_count(NodeName *phead)
{
    NodeName *el;
    int count = 0;
    LL_COUNT(phead, el, count);
    return count;
}




//   ==================================

int getProcessName(char *szName, int nMaxLen)
{
    char szProcName[MAX_FILEPATHNAME_LEN] = {0};
    int rval = readlink("/proc/self/exe", szProcName, MAX_FILEPATHNAME_LEN-1);
    if(rval < 0)
    {
        Safe_Printf("error read link ret = %d", rval);
        return -1;
    }
    // remove path : /usr/bin/
    char *pBar = strrchr(szProcName, '/');
    if(NULL == pBar)
    {
        Safe_Printf("error name=%s", szProcName);
        return -1;
    }
    pBar = pBar + 1;
    snprintf(szName, nMaxLen, "%s", pBar);
    return 0;
}

int isProcRunning(int pid)
{
    char szDir[32] = {0};
    snprintf(szDir,32, "/proc/%d", pid);
    return access(szDir, F_OK); // F_OK exist
}

void test_namelist()
{
    int ret = -1;
    Safe_Printf("g_mon=%p,%d", g_monitorXmlHead, ret);
    Safe_Printf("count=%d", namelist_count(g_monitorXmlHead));

    ret = namelist_uniadd(&g_monitorXmlHead, CONF_NETWORKSET, 100);
    Safe_Printf("g_mon=%p,%d", g_monitorXmlHead, ret);
    Safe_Printf("count=%d", namelist_count(g_monitorXmlHead));

    namelist_print(g_monitorXmlHead); 

    Safe_Printf("count=%d", namelist_count(g_monitorXmlHead));
    Safe_Printf("count=%d", namelist_count(g_monitorXmlHead));

    Safe_Printf("xml_loadflag = %d", sizeof(ShmInfo));
    Safe_Printf("page.size=%d", getpagesize());

}

int loadflags_init()
{
    return 0;
}

int loadflags_uninit()
{
    return 0;
}

//  need check this xml
int loadflags_test(const char *xmlfilename)
{
    char *xmlname = filenamestrippath(xmlfilename);
    int xmlIndex  = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    Safe_Printf("xml:%d,%s. proc=%d", xmlIndex, xmlname, procIndex);
    shmem_printmem();
    sleep(10000);
    return 0;
}

// notify other process reload
int loadflags_clearAllProc(const char *xmlfilename)
{
    char *xmlname = filenamestrippath(xmlfilename);
    int xmlIndex  = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    shmem_clearxmlload(xmlname);
    (void)xmlIndex;
    (void)procIndex;
    // Safe_Printf("xml:%d,%s, proc=%d", xmlIndex, xmlname, procIndex);
    shmem_printmem();
    return 0;
}

// set this xml is load
int loadflags_set(const char *xmlfilename)
{
    char *xmlname = filenamestrippath(xmlfilename);
    int xmlIndex  = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    shmem_setxmlload(xmlname);
    (void)xmlIndex;
    (void)procIndex;
//    Safe_Printf("xml:%d,%s, proc=%d", xmlIndex, xmlname, procIndex);
    shmem_printmem();
    return 0;
}

// get this xml's loadflag
int loadflags_get(const char *xmlfilename)
{
    char *xmlname = filenamestrippath(xmlfilename);
    int xmlIndex = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    int flag = shmem_getxmlload(xmlname);
    (void)xmlIndex;
    (void)procIndex;
    //Safe_Printf("xml:%d,%s, proc=%d,flag=%d", xmlIndex, xmlname, procIndex,flag);
    shmem_printmem();
    return flag;
}


// ======= shmem


void *shmem_getmem()
{
    static int   s_shmemId = -1;
    extern int errno;
    if(s_shmemId < 0)
    {
        semproc_lock();
        ShmInfo *pdata = NULL;
        int shmemId = -1;
        int nMemSize = sizeof(ShmInfo);
        do{
            shmemId = shmget(SHMEM_KEY, nMemSize, IPC_CREAT|0660);
            Safe_Printf("shmget.size=%d, shmemid=%d", nMemSize, shmemId);
            if(shmemId < 0)
            {
                Safe_Printf("error shmId=%d, err=%d,%s", shmemId, errno, strerror(errno));
                break;
            }
            pdata = (ShmInfo *)shmat(shmemId, 0, 0);
            Safe_Printf("shmid=%d, shm at %p", shmemId, pdata);
            if(NULL == pdata)
            {
                Safe_Printf("error pdata null");
                break;
            }

        }while(0);
        semproc_unlock();
        if(shmemId < 0 || NULL == pdata)
        {
            return NULL;
        }
        s_shmemId = shmemId;
    }
    if(s_shmemId < 0)
    {
        Safe_Printf("error shmId");
        return NULL;
    }
    void *pdata = shmat(s_shmemId, 0, 0);
    return pdata;
}


int shmem_printmem()
{
    if(0 == SHMEM_DEBUG_PRINT)
    {
        return -1;
    }
    ShmInfo *pShmInfo = (ShmInfo *)shmem_getmem();
    if(NULL == pShmInfo)
    {
        Safe_Printf("error shm get null");
        return -1;
    }
    char szContent[1024] = {0};
    char szTmp[1024]     = {0};
    char szOutput[1024]  = {0};
    // print proc
    snprintf(szContent,sizearray(szContent), "proc:  ");
    for(int i=0; i < MAX_PROCESS_COUNT; i++)
    {
        snprintf(szTmp, sizearray(szTmp), "%s", szContent);
        snprintf(szContent, sizearray(szContent), "%s[%d,%s,%d] ", szTmp, i, pShmInfo->procname[i], pShmInfo->pid[i]);
    }
    snprintf(szTmp, sizearray(szTmp), "%s", szOutput);
    snprintf(szOutput, sizearray(szOutput), "%s\n%s", szTmp, szContent);

    // print xml
    snprintf(szContent, sizearray(szContent), " xml:  ");
    for(int i=0; i < MAX_XML_COUNT; i++)
    {
        snprintf(szTmp, sizearray(szTmp), "%s", szContent);
        snprintf(szContent, sizearray(szContent), "%s[%d,%s] ", szTmp, i, pShmInfo->xmlname[i]);
    }
    snprintf(szTmp, sizearray(szTmp), "%s", szOutput);
    snprintf(szOutput, sizearray(szOutput), "%s\n%s", szTmp, szContent);

    // print loadflags
    int *ploadflag = &pShmInfo->loadflags[0][0];
    for(int i=0; i < MAX_XML_COUNT; i++)
    {
        szContent[0] = '\0';
        for(int j=0; j < MAX_PROCESS_COUNT; j++)
        {
            snprintf(szTmp,     sizearray(szTmp),     "%s", szContent);
            snprintf(szContent, sizearray(szContent), "%s %4d, ", szTmp, *(ploadflag+i*MAX_PROCESS_COUNT+j));
        }
        char szxml_t[16] = {0};
        snprintf(szxml_t, sizearray(szxml_t), "%s........................", pShmInfo->xmlname[i]);

        snprintf(szTmp,     sizearray(szTmp), "xmlid:%3d %s --  %s", i, szxml_t, szContent);
        snprintf(szContent, sizearray(szContent), "%s", szTmp);

        snprintf(szTmp, sizearray(szTmp), "%s", szOutput);
        snprintf(szOutput, sizearray(szOutput), "%s\n%s", szTmp, szContent);
    }
    Safe_Printf("%s", szOutput);
    return 0;
}


int shmem_bzero()
{
    //test
    /*
    bzero(pdata, nMemSize);
    int i=0;
    snprintf(pdata->procname[i], MAX_NAME_LEN, "boghe");      pdata->pid[i] = 11;  i++;
    snprintf(pdata->procname[i], MAX_NAME_LEN, "cwmp");       pdata->pid[i] = 12;  i++;
    snprintf(pdata->procname[i], MAX_NAME_LEN, "update_udp"); pdata->pid[i] = 13;  i++;
    
    for(int i=0; i<MAX_XML_COUNT; i++)
        for(int j=0; j<MAX_PROCESS_COUNT; j++)
            pdata->loadflags[i][j] = 1;
    shmem_printmem();
    */
    return 0;
}


int shmem_clearxmlload(const char *xmlname)
{
    ShmInfo *pShmInfo = (ShmInfo *)shmem_getmem();
    if(NULL == pShmInfo)
    {
        Safe_Printf("error shm get null");
        return -1;
    }
    int xmlIndex  = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    if(procIndex < 0 || xmlIndex < 0)
    {
        Safe_Printf("error shm get xml:%d,%s, proc=%d", xmlIndex, xmlname, procIndex);
        return -1;
    }
    semproc_lock();
    // minus 1
    for(int i=0; i<MAX_XML_COUNT; i++)
    {
        if(xmlIndex != i)
            continue;
        for(int j=0; j<MAX_PROCESS_COUNT; j++)
        {
            pShmInfo->loadflags[i][j]--;    //  pShmInfo->loadflags[i][j] = 0;
        }
    }
    // set 0
//    memset(&pShmInfo->loadflags[0][0] + xmlIndex * MAX_PROCESS_COUNT, 0, sizeof(pShmInfo->loadflags[0][0]) * MAX_PROCESS_COUNT );
    semproc_unlock();
    return 0;
}

int shmem_setxmlload(const char * xmlname)
{
    ShmInfo *pShmInfo = (ShmInfo *)shmem_getmem();
    if(NULL == pShmInfo)
    {
        Safe_Printf("error shm get null");
        return -1;
    }
    int xmlIndex  = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    if(procIndex < 0 || xmlIndex < 0)
    {
        Safe_Printf("error shm get xml:%d,%s, proc=%d", xmlIndex, xmlname, procIndex);
        return -1;
    }
    semproc_lock();
    pShmInfo->loadflags[xmlIndex][procIndex] = 1;
    semproc_unlock();
    return 0;
}

int shmem_getxmlload(const char *xmlname)
{
    ShmInfo *pShmInfo = (ShmInfo *)shmem_getmem();
    if(NULL == pShmInfo)
    {
        Safe_Printf("error shm get null");
        return -1;
    }
    int xmlIndex  = shmem_getxmlIndex(xmlname);
    int procIndex = shmem_getprocIndex();
    if(procIndex < 0 || xmlIndex < 0)
    {
        Safe_Printf("error shm get xml:%d,%s, proc=%d", xmlIndex, xmlname, procIndex);
        return -1;
    }
    semproc_lock();
    int nflag = pShmInfo->loadflags[xmlIndex][procIndex];
    semproc_unlock();
    return nflag;
}


int shmem_getxmlIndex(const char *xmlname)
{
    NodeName *pout = NULL;
    int ret = namelist_find(g_monitorXmlHead, xmlname, &pout);
    if(ret < 0 || NULL == pout)
    {
        int nxmlid = shmem_addxmlname(xmlname);
        if(nxmlid < 0)
        {
            Safe_Printf("error nxmlid=%d,xml=%s", nxmlid, xmlname);
            return -1;
        }
        int ret = namelist_uniadd(&g_monitorXmlHead, xmlname, nxmlid);
        if(ret < 0)
        {
            Safe_Printf("error name add %s,ret=%d", xmlname, ret);
            return -1;
        }
        ret = namelist_find(g_monitorXmlHead, xmlname, &pout);
        if(ret < 0 || NULL == pout)
        {
            Safe_Printf("error name find %s,ret=%d", xmlname, ret);
            return -1;
        }
    }
    return pout->Id;
}

int shmem_getprocIndex()
{
    static int s_procIndex = -1;
    if(s_procIndex < 0)
    {
        char szName[MAX_NAME_LEN] = {0};
        getProcessName(szName, MAX_NAME_LEN);
        s_procIndex = shmem_addprocname(szName, getpid());
    }
    return s_procIndex;
}

int shmem_uniaddname(void *paddr, const int nMaxNameCount, const char *addname, int *paddrId, const int pid)
{
    if(NULL == paddr)
    {
        Safe_Printf("paddr=null");
        return -1;
    }
    // check exist and find blank
    int nExistPos = -1;
    int nNewPos = -1;
    for(int i=0; i < nMaxNameCount ; i++)
    {
        char *nameTmp = paddr + i * MAX_NAME_LEN;
        // is empty name
        if(0 == nameTmp[0])
        {
            nNewPos = i;
            break;
        }
        // is diff name
        if(0 != strcmp(nameTmp, addname))
        {
            continue ;
        }
        // is same name xml
        if(pid <= 0 || NULL == paddrId)
        {
            nExistPos = i;
            break;
        }
        // check proc in shmem
        int pidtmp = paddrId[i];
        // is same name proc and alive
        if(isProcRunning(pidtmp) == 0)
        {
            continue ;
        }
        // is same name proc but die, take pos
        Safe_Printf("%s,%s,  pid=%d,%d, %d", nameTmp, addname, pidtmp,pid,  isProcRunning(pidtmp) );
        nNewPos = i;
        break;
    }
    if(nExistPos >= 0)
    {
        Safe_Printf("%s exist at %d", addname, nExistPos);
        return nExistPos;
    }
    if(nNewPos < 0)
    {
        Safe_Printf("error shmem  name list is full, %s", addname);
        return -1;
    }
    // add name
    snprintf(paddr + nNewPos * MAX_NAME_LEN, MAX_NAME_LEN, "%s", addname);
    if(paddrId && pid > 0)
    {
        paddrId[nNewPos] = pid;
    }
    Safe_Printf("addname=%s,%d,pos=%d", addname, pid, nNewPos);
    return nNewPos;
}

int shmem_addprocname(const char *addname, int pid)
{
    ShmInfo *pdata = (ShmInfo *)shmem_getmem();
    if(NULL == pdata)
    {
        Safe_Printf("error pdata");
        return -1;
    }
    semproc_lock();
    int nprocindex = shmem_uniaddname(&pdata->procname[0][0], MAX_PROCESS_COUNT, addname, &pdata->pid[0], pid);
    semproc_unlock();
    return nprocindex;
}

int shmem_addxmlname(const char *addname)
{
    ShmInfo *pdata = (ShmInfo *)shmem_getmem();
    if(NULL == pdata)
    {
        Safe_Printf("error shmem pdata");
        return -1;
    }
    semproc_lock();
    int nid = shmem_uniaddname(&pdata->xmlname[0][0], MAX_XML_COUNT, addname, NULL, 0);
    semproc_unlock();
    return nid;
}




